﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Linq;
using System.Windows.Controls;

namespace HobSoft.SoccerBet2014.Client.ViewModels
{
    [Serializable]
    public abstract class ViewModelBase : INotifyPropertyChanged, INotifyPropertyChanging, INotifyDataErrorInfo
    {
        public ViewModelBase()
        {
            Initialize();
        }

        private bool _onInitialized = false;
        public bool OnInitialized
        {
            get { return _onInitialized; }
            private set { PropertyValueChange(ref _onInitialized, value, "PropertyValueChange"); }
        }

        private ModelState _state;

        public ModelState State
        {
            get { return _state; }
            protected set { PropertyValueChange(ref _state, value, "State"); }
        }


        protected virtual void Initialize()
        {
            OnInitialized = true;
            errorsContainer = new ModelErrorsContainer(pn => RaiseErrorsChanged(pn));
            OnInitializeOver();
        }

        protected virtual void OnInitializeOver()
        {

        }

        #region error info
        [NonSerialized]
        protected ModelErrorsContainer errorsContainer;

        [field: NonSerialized]
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        public IEnumerable GetErrors(string propertyName)
        {
            return errorsContainer.GetErrors(propertyName);
        }

        public bool HasErrors
        {
            get { return errorsContainer != null ? errorsContainer.HasErrors : false; }
        }

        public string LastErrorMessage
        {
            get
            {
                var errors = errorsContainer.GetErrors(LastErrorProperty);
                if (errors == null || !errors.Any()) return string.Empty;
                return errors.LastOrDefault().ErrorContent.ToString();
            }
        }

        public string LastErrorProperty
        {
            get;
            protected set;
        }

        private void RaiseErrorsChanged(string propertyName)
        {
            var handler = this.ErrorsChanged;
            LastErrorProperty = propertyName;
            RaisePropertyChanged("LastErrorProperty");
            RaisePropertyChanged("LastErrorMessage");
            RaisePropertyChanged("HasErrors");
            if (handler != null)
            {
                handler(this, new DataErrorsChangedEventArgs(propertyName));
            }
        }

        protected void RaiseErrorsChanged(string propertyName, string message)
        {
            errorsContainer.SetErrors(propertyName, new ValidationResult[] { new ValidationResult(false, message) });
        }

        protected void RaiseErrorsChanged(string propertyName, params ValidationResult[] validationResults)
        {
            errorsContainer.SetErrors(propertyName, validationResults);
        }
        #endregion

        #region NotifyPropertyChange
        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;
        [field: NonSerialized]
        public event PropertyChangingEventHandler PropertyChanging;

        protected virtual void PropertyValueChange<T>(ref T source, T newValue, string sourceName)
        {
            Type t = typeof(T);
            if (t.IsValueType || t.IsEnum)
            {
                if (!source.Equals(newValue))
                {
                    if (PropertyChanging != null)
                        PropertyChanging(this, new PropertyChangingEventArgs(sourceName));

                    source = newValue;

                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs(sourceName));
                }
            }
            else
            {
                if (source == null && newValue == null)
                    return;
                if (source == null)
                {
                    if (PropertyChanging != null)
                        PropertyChanging(this, new PropertyChangingEventArgs(sourceName));

                    source = newValue;

                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs(sourceName));
                }
                else
                {
                    if (!source.Equals(newValue))
                    {
                        if (PropertyChanging != null)
                            PropertyChanging(this, new PropertyChangingEventArgs(sourceName));

                        source = newValue;

                        if (PropertyChanged != null)
                            PropertyChanged(this, new PropertyChangedEventArgs(sourceName));
                    }
                }
            }
        }

        protected virtual void RaisePropertyChanged(string sourceName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(sourceName));
            }
        }

        protected virtual void RaisePropertyChanging(string sourceName)
        {
            if (PropertyChanging != null)
            {
                PropertyChanging(this, new PropertyChangingEventArgs(sourceName));
            }
        }
        #endregion
    }

    public enum ModelState : byte
    {
        Freeing,
        Executing,
        Initializing
    }
}
